/*************************************************************/
/*                                                           */
/*     MC823 - Laboratorio de Teleprocessamento e Redes      */
/*     1 Semestre / 2013                                     */
/*     Projeto 1 - Cliente e Servidor UDP de uma Livraria    */
/*                                                           */
/*     Alunos: Joao Gabriel M.D.Mendes - RA 102788           */
/*             Lucas Domingues Lima    - RA 103140           */
/*                                                           */
/*************************************************************/

/* Arquivo: /servidor/udp/servidor.c
   Definicao: Implementa funcoes principais do servidor em si,
                          gerando o executavel final.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "../../sendrecv.h"
#include "../livraria.h"
#include "../manipulamsgs.h"
#include <time.h>

#define MYPORT "4950"	// the port users will be connecting to

/* NANO_PER_SECOND define o numero de nanosegundos em um segundo */
#define NANO_PER_SECOND	1000000000

/* get sockaddr, IPv4 or IPv6: */
void *get_in_addr(struct sockaddr *sa)
{
	if (sa->sa_family == AF_INET) {
		return &(((struct sockaddr_in*)sa)->sin_addr);
	}

	return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(int argc,char **argv)
{
	int sockfd;
	struct addrinfo hints, *servinfo, *p;
	int rv;
	int numbytes,out;
	struct sockaddr_storage their_addr;
	char *buf,*aux,*id,*req,arquivoLog[50],arquivoTempo[50],*ip,ipid[50];
	size_t addr_len;
	char s[INET6_ADDRSTRLEN];
	t_estoque *estoque;
	FILE *arqLog,*arqTempo;
	struct timespec tstart;
	struct timespec tstop;
	float driftnano;

	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
	hints.ai_socktype = SOCK_DGRAM;
	hints.ai_flags = AI_PASSIVE; // use my IP
	addr_len = sizeof their_addr;

	if ((rv = getaddrinfo(NULL, MYPORT, &hints, &servinfo)) != 0) {
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
		return 1;
	}

	/* loop through all the results and bind to the first we can */
	for(p = servinfo; p != NULL; p = p->ai_next) {
		if ((sockfd = socket(p->ai_family, p->ai_socktype,
				p->ai_protocol)) == -1) {
			perror("server: socket");
			continue;
		}

		if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
			close(sockfd);
			perror("server: bind");
			continue;
		}

		break;
	}

	if (p == NULL) {
		fprintf(stderr, "server: failed to bind socket\n");
		return 2;
	}

	freeaddrinfo(servinfo);

	printf("server: waiting to recvfrom...\n");

    estoque = criaEstoque();

    while(1)
    {
	/*recebe mensagem via UDP*/
	out = recvallfrom(sockfd,&buf,&numbytes,(struct sockaddr *)&their_addr,&addr_len);
	/*se houver erro geral*/
        if (out == -1) 
        {
	    perror("recvallfrom");
	    exit(1);
	}
	/*se houver timeout*/
        else if(out == -2)
        {
            printf("server: timeout\n");
        }
        else
	/*se houver mensagem*/
        {
	    /*os primeiros 8 digitos sao o identificador do cliente*/
            id = retornaSubstring(buf,0,7);
	    /*os digitos restantes compoem a mensagem/requisicao*/
            req = retornaSubstring(buf,8,strlen(buf));
	    ip = inet_ntop(their_addr.ss_family,get_in_addr((struct sockaddr *)&their_addr),s, sizeof s);
	    
	    /*abre arquivos de log*/
	    sprintf(ipid,"%s::%s",ip,id);
            sprintf(arquivoLog,"log/%s",id);
            sprintf(arquivoTempo,"tempo/%s",id);
            arqLog = fopen(arquivoLog,"a");
            arqTempo = fopen(arquivoTempo,"a");
            fprintf(arqLog,"Cliente: %s\n",req);
            /* Primeiro, obtenho o tempo inicial. */
	    clock_gettime(CLOCK_REALTIME, &tstart);
	    
	    /*faz operacao solicitada pelo cliente*/
	    aux = trataRequisicaoCliente(estoque,req);
            
	    /* Para obter o tempo final */
	    clock_gettime(CLOCK_REALTIME, &tstop);
            /* Calcula-se o tempo gasto */
	    driftnano = (float)(tstop.tv_sec - tstart.tv_sec);
	    driftnano += (tstop.tv_nsec - tstart.tv_nsec)/(float)NANO_PER_SECOND;
	    
	    /*escreve no log*/
            fprintf(arqLog,"Servidor: %s\n",aux);
	    fprintf(arqTempo,"%d %.9f\n",req[0]-'A'+1,driftnano);
	    numbytes = strlen(aux);
            if (sendallto(sockfd, aux, &numbytes, (struct sockaddr *)&their_addr, addr_len) == -1)
		  perror("sendallto");
            
 
	    /*libera memoria utilizada*/
            free(aux);
            free(buf);
            free(id);
            free(req);
            fclose(arqLog);
            fclose(arqTempo);
        }
    }

    close(sockfd);

    return 0;
}
